/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact 824069438@qq.com
 *  
 */
package org.zfes.snowy.auth.shiro.filter.csrf;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.zfes.snowy.auth.shiro.exceptions.CsrfTokenException;
import org.zfes.snowy.auth.shiro.exceptions.InvalidTokenException;
import org.zfes.snowy.auth.shiro.handlers.IAuthecFailureHandler;
import org.zfes.snowy.auth.shiro.jwt.token.JWTTokenParser;
import org.zfes.snowy.auth.shiro.util.JWTTokenVerifyPublicKeyUtil;
import org.zfes.snowy.core.consts.FrameConst;
import org.zfes.snowy.core.exceptions.AppRuntimeException;
import org.zfes.snowy.core.util.ZStrUtil;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
/**
 * csrf token 验证拦截器：<br>
 * setp1:用户登录后产生csrf token ，名称 ： csrf-access-token ；前端放置于localstore中<br><br>
 * setp2:所有请求必须在header或者param中添加csrf-access-token 参数<br><br>
 * -----------------------------------------<br><br>
 * A: 首先在部署系统之前通过 TokenPubkeyUtil--createCsrfVerifyPublicKey 产生csrftoken-（uuid）<br><br>
 * B: csrftoken作为数据段通过RSA 加密，私钥保存到signaturekey/rsa_csrf_privateKey<br><br>
 * C: csrftoken作为数据段通过RSA 加密，加密后数据保存到signaturekey/rsa_csrf_data.dat<br><br>
 * D: 部署系统之前 需要手动将signaturekey 文件夹 放到每个tomcat 实例class目录下，<br><br>
 * E: 系统启动后，读取数据段和私钥，解密 csrftoken<br><br>
 * F: 解密后的csrftoken 作为JWT 中SH512对称加密的公钥，对客户端提交的 csrf-access-token 进行校验<br><br>
 * 
 * @author zhaohuatai
 *
 */
public class CSRFTokenFilter extends AccessControlFilter{

	private String csrfTokenName;
	
	private IAuthecFailureHandler csrfAuthenticationFailureHandler;
	
	
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
		return false;
	}
	

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		 String url=WebUtils.toHttp(request).getRequestURL().toString();
	   		System.out.println("CSRFTokenFilter||"+url);
		if(!SecurityUtils.getSubject().isAuthenticated()){
			//未登录 请求不予处理
			return true; 
		}
		HttpServletRequest httpRequest =WebUtils.toHttp(request);
		HttpServletResponse httpResponse =WebUtils.toHttp(response);
		if(ZStrUtil.hasText(getCsrfTokenName())){
			csrfTokenName=FrameConst.TOKENS.CSRF_TOKEN;
		}
	    String jwtTokenStr=JWTTokenParser.getTokenFromRequest(httpRequest,csrfTokenName);
	    try {  
	    	
	    	validateCsrfToken(jwtTokenStr);
	    	
		} catch (AuthenticationException e) {  
			csrfAuthenticationFailureHandler.onAuthenticationFailure(httpRequest, httpResponse, e);
		   return false;  
		 } 
	    return true; 
	}
	
	private  void  validateCsrfToken(String tokenStr) {
		if(!ZStrUtil.hasText(tokenStr)){
    		throw new InvalidTokenException("the csrf token is empty or null.");
    	}
		String csrfVerifyPublicKey=JWTTokenVerifyPublicKeyUtil.readCsrfVerifyPublicKey();
		if(!ZStrUtil.hasText(csrfVerifyPublicKey)){
			throw new AppRuntimeException("the csrf verify Public Key should be generated before this app deployed ");
		}
    	try {
		    Jwts.parser().setSigningKey(csrfVerifyPublicKey).parse(tokenStr);
		} catch (SignatureException e) {
			throw new CsrfTokenException("Csrf token error");
		}
	}


	public IAuthecFailureHandler getCsrfAuthenticationFailureHandler() {
		return csrfAuthenticationFailureHandler;
	}


	public void setCsrfAuthenticationFailureHandler(IAuthecFailureHandler csrfAuthenticationFailureHandler) {
		this.csrfAuthenticationFailureHandler = csrfAuthenticationFailureHandler;
	}


	public String getCsrfTokenName() {
		return csrfTokenName;
	}


	public void setCsrfTokenName(String csrfTokenName) {
		this.csrfTokenName = csrfTokenName;
	}
	

	

}
